# -*- coding: utf-8 -*-
# UDP datagram parsing
# $Id: parser.py 91 2012-03-01 09:52:58Z 4glitch $

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import threads
from datetime import datetime
import re

time_re = re.compile("""
      # day of week
    ((Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s+)?
    (?P<datetime>(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+
      # day
    ([0-2]?[0-9]|3[0-1])\s
      # Edge-CorE switches send time like 0:1:16
    (?P<time>([0-1]?[0-9]|2[0-4]):[0-5]?[0-9]:[0-5]?[0-9])
      # milisecond from cisco router for example
    (\.\d{3})?
    (\s+(?P<year>[2-9]\d{3}))?)
    """, re.VERBOSE)

#from twisted.python import log


class UDPSyslog(DatagramProtocol):
    """ The protocol to parse syslog UDP datagram. """
    
    def __init__(self, action):
        """ action invoked with dictionary of syslog values. """
        self.accept = action
    
    def datagramReceived(self, msg, (host, port)):
        
        values = self._split_datagram(msg) # this may be done with deferred
        if values: # can't decide how to resolve here
            values.update({'host': host})
            self.accept(values)
    
    def _split_datagram(self, msg):
        if msg[0] == '<':
            next = msg.find('>', 1, 5)
            if next > 0:
                now = datetime.now()
                pri, msg = int(msg[1:next]), msg[next+1:]
                
                match = time_re.match(msg)
                if match and match.start() == 0: # start?
                    msg, match = msg[match.end()+1:], match.groupdict()
                    
                    time = match['datetime']
                    if not match['year']:
                        time += ' %s' % now.year
                    time = datetime.strptime(time, '%b %d %H:%M:%S %Y')
                else:
                    time = now
                
                next = msg.find(':')
                pid = None
                # is it really no whitespace
                if next > 0 and msg[:next].find(' ') == -1:
                    program, msg = msg[:next], msg[next+1:].strip()
                    try:
                        program, pid = program.split('[')
                        pid, rest = pid.split(']')
                        if rest != '': raise ValueError
                    except ValueError:
                        pass
                else:
                    program = ''
            
            return {"severity": pri & 7,
                    "facility": (pri & 248) >> 3,
                    "time": time,
                    "program": program,
                    "pid": pid,
                    "message": msg}
